// `output` print variant error strings to either stderr or stdout.
// For fatal errors, print to stderr;
// and for others, e.g. docopt version info, print to stdout.
-fn output(caption: Option<String>, detail: Option<String>,
+fn output(caption: Option<&str>, detail: Option<String>,
shell: &mut MultiShell, fatal: bool) {
let std_shell = if fatal {shell.err()} else {shell.out()};
if let Some(caption) = caption {
log!(4, "handle_error; err={:?}", err);
let CliError { error, exit_code, unknown } = err;
- let verbose = shell.get_verbose();
let fatal = exit_code != 0; // exit_code == 0 is non-fatal error
+
+ let desc = error.description();
if unknown {
- output(Some("An unknown error occurred".to_string()), None, shell, fatal);
- } else if error.to_string().len() > 0 {
- output(Some(error.to_string()), None, shell, fatal);
+ output(Some("An unknown error occurred"), None, shell, fatal);
+ } else if desc.len() > 0 {
+ output(Some(desc), None, shell, fatal);
}
-
- if error.cause().is_some() || unknown {
- if !verbose {
- output(None,
- Some("\nTo learn more, run the command again with --verbose.".to_string()),
- shell, fatal);
- }
+ if shell.get_verbose() {
+ output(None, error.detail(), shell, fatal);
}
-
- if verbose {
- if unknown {
- output(Some(error.to_string()), None, shell, fatal);
- }
- if let Some(detail) = error.detail() {
- output(None, Some(detail), shell, fatal);
- }
- if let Some(err) = error.cause() {
- let _ = handle_cause(err, shell);
- }
+ if !handle_cause(&*error, shell) {
+ let _ = shell.err().say("\nTo learn more, run the command again \
+ with --verbose.".to_string(), BLACK);
}
std::os::set_exit_status(exit_code as isize);
}
-fn handle_cause(mut err: &Error, shell: &mut MultiShell) {
+fn handle_cause(mut cargo_err: &CargoError, shell: &mut MultiShell) -> bool {
+ let verbose = shell.get_verbose();
+ let mut err;
loop {
- let _ = shell.err().say("\nCaused by:", BLACK);
- let _ = shell.err().say(format!(" {}", err.description()), BLACK);
+ cargo_err = match cargo_err.cargo_cause() {
+ Some(cause) => cause,
+ None => { err = cargo_err.cause(); break }
+ };
+ if !verbose && !cargo_err.is_human() { return false }
+ print(cargo_err.description(), cargo_err.detail(), shell);
+ }
+ loop {
+ let cause = match err { Some(err) => err, None => return true };
+ if !verbose { return false }
+ print(cause.description(), cause.detail(), shell);
+ err = cause.cause();
+ }
- match err.cause() {
- Some(e) => err = e,
- None => break,
+ fn print(desc: &str, detail: Option<String>, shell: &mut MultiShell) {
+ let _ = shell.err().say("\nCaused by:", BLACK);
+ let _ = shell.err().say(format!(" {}", desc), BLACK);
+ if shell.get_verbose() {
+ if let Some(detail) = detail {
+ let _ = shell.err().say(detail, BLACK);
+ }
}
}
}
pub trait CargoError: Error {
fn is_human(&self) -> bool { false }
+ fn cargo_cause(&self) -> Option<&CargoError>{ None }
}
impl fmt::String for Box<CargoError> {
impl CargoError for Box<CargoError> {
fn is_human(&self) -> bool { (**self).is_human() }
+ fn cargo_cause(&self) -> Option<&CargoError> { (**self).cargo_cause() }
}
// =============================================================================
struct ChainedError<E> {
error: E,
- cause: Box<Error>,
+ cause: Box<CargoError>,
}
impl<'a, T, F> ChainError<T> for F where F: FnOnce() -> CargoResult<T> {
}
}
-impl<T, E: Error> ChainError<T> for Result<T, E> {
+impl<T, E: CargoError> ChainError<T> for Result<T, E> {
fn chain_error<E2, C>(self, callback: C) -> CargoResult<T>
where E2: CargoError, C: FnOnce() -> E2 {
self.map_err(move |err| {
impl<E: Error> Error for ChainedError<E> {
fn description(&self) -> &str { self.error.description() }
fn detail(&self) -> Option<String> { self.error.detail() }
- fn cause(&self) -> Option<&Error> { Some(&*self.cause) }
}
impl<E: CargoError> CargoError for ChainedError<E> {
fn is_human(&self) -> bool { self.error.is_human() }
+ fn cargo_cause(&self) -> Option<&CargoError> { Some(&*self.cause) }
}
// =============================================================================
fn cause(&self) -> Option<&Error> { self.0.cause() }
}
-impl<E: Error> CargoError for Human<E> {
+impl<E: CargoError> CargoError for Human<E> {
fn is_human(&self) -> bool { true }
+ fn cargo_cause(&self) -> Option<&CargoError> { self.0.cargo_cause() }
}
// =============================================================================
toml::DecodeError,
}
-impl<E: Error> FromError<Human<E>> for Box<CargoError> {
+impl<E: CargoError> FromError<Human<E>> for Box<CargoError> {
fn from_error(t: Human<E>) -> Box<CargoError> { Box::new(t) }
}
impl CargoError for toml::Error {}
impl CargoError for toml::DecodeError {}
impl CargoError for url::ParseError {}
+impl CargoError for str::Utf8Error {}
// =============================================================================
// Construction helpers
}));
let root = try!(parse(contents, &manifest));
let mut d = toml::Decoder::new(toml::Value::Table(root));
- let toml_manifest: TomlManifest = try!(Decodable::decode(&mut d));
+ let manifest: TomlManifest = try!(Decodable::decode(&mut d).map_err(|e| {
+ human(e.to_string())
+ }));
- let pair = try!(toml_manifest.to_manifest(source_id, &layout, config));
+ let pair = try!(manifest.to_manifest(source_id, &layout, config));
let (mut manifest, paths) = pair;
match d.toml {
Some(ref toml) => add_unused_keys(&mut manifest, toml, "".to_string()),
.with_status(101)
.with_stderr("\
failed to parse manifest at `[..]`
-Cargo.toml is not a valid manifest
-No `package` or `project` section found.
+Caused by:
+ No `package` or `project` section found.
"))
});
.with_status(101)
.with_stderr("\
failed to parse manifest at `[..]`
-could not parse input as TOML
+
+Caused by:
+ could not parse input as TOML
Cargo.toml:3:19-3:20 expected a value
"))
.with_status(101)
.with_stderr("\
failed to parse manifest at `[..]`
-could not parse input as TOML\n\
+
+Caused by:
+ could not parse input as TOML\n\
src[..]Cargo.toml:1:5-1:6 expected a value\n\n"))
});
.with_status(101)
.with_stderr("\
failed to parse manifest at `[..]`
-Cargo.toml is not a valid manifest
-cannot parse '1.0' as a semver for the key `project.version`
+Caused by:
+ cannot parse '1.0' as a semver for the key `project.version`
"))
});
execs().with_status(101)
.with_stderr("\
failed to parse manifest at `[..]Cargo.toml`
-either a [lib] or [[bin]] section must be present\n"));
+
+Caused by:
+ either a [lib] or [[bin]] section must be present\n"));
});
test!(lto_build {
execs().with_status(101)
.with_stderr("\
failed to parse manifest at `[..]`
-either a [lib] or [[bin]] section must be present"));
+
+Caused by:
+ either a [lib] or [[bin]] section must be present"));
});
test!(shared_dep_with_a_build_script {
.with_stdout("")
.with_stderr(format!("\
failed to parse manifest at `[..]`
-Cargo.toml is not a valid manifest
-invalid url `{}`: relative URL without a base
+Caused by:
+ invalid url `{}`: relative URL without a base
", url)));
});
assert_that(p.cargo_process("build"),
execs().with_status(101).with_stderr(format!("\
failed to parse manifest at `[..]`
-Cargo.toml is not a valid manifest
-Feature `bar` includes `baz` which is neither a dependency nor another feature
+Caused by:
+ Feature `bar` includes `baz` which is neither a dependency nor another feature
").as_slice()));
});
assert_that(p.cargo_process("build"),
execs().with_status(101).with_stderr(format!("\
failed to parse manifest at `[..]`
-Cargo.toml is not a valid manifest
-Features and dependencies cannot have the same name: `bar`
+Caused by:
+ Features and dependencies cannot have the same name: `bar`
").as_slice()));
});
assert_that(p.cargo_process("build"),
execs().with_status(101).with_stderr(format!("\
failed to parse manifest at `[..]`
-Cargo.toml is not a valid manifest
-Feature `bar` depends on `baz` which is not an optional dependency.
+Caused by:
+ Feature `bar` depends on `baz` which is not an optional dependency.
Consider adding `optional = true` to the dependency
").as_slice()));
});
assert_that(p.cargo_process("build"),
execs().with_status(101).with_stderr(format!("\
failed to parse manifest at `[..]`
-Cargo.toml is not a valid manifest
-Dev-dependencies are not allowed to be optional: `bar`
+Caused by:
+ Dev-dependencies are not allowed to be optional: `bar`
").as_slice()));
});
assert_that(p.cargo_process("build").arg("--features").arg("foo"),
execs().with_status(101).with_stderr(format!("\
failed to parse manifest at `[..]`
-Cargo.toml is not a valid manifest
-Feature `foo` requires `bar` which is not an optional dependency
+Caused by:
+ Feature `foo` requires `bar` which is not an optional dependency
").as_slice()));
});
assert_that(p.cargo_process("build").arg("--features").arg("foo"),
execs().with_status(101).with_stderr(format!("\
failed to parse manifest at `[..]`
-Cargo.toml is not a valid manifest
-Feature `foo` requires `bar` which is not an optional dependency
+Caused by:
+ Feature `foo` requires `bar` which is not an optional dependency
").as_slice()));
});